// ==++==
// 
//   
//    Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
//   
//    The use and distribution terms for this software are contained in the file
//    named license.txt, which can be found in the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by the
//    terms of this license.
//   
//    You must not remove this notice, or any other, from this software.
//   
// 
// ==--==
/*============================================================
**
** File:    remoting.h
**       
**
** Purpose: Defines various remoting related objects such as
**          proxies
**
** Date:    Feb 16, 1999
**
===========================================================*/
#ifndef __REMOTING_H__
#define __REMOTING_H__

#include "fcall.h"
#include "stubmgr.h"


// Forward declaration
class TPMethodFrame;


// Thunk hash table - the keys are MethodDesc
typedef EEHashTable<MethodDesc *, EEPtrHashTableHelper<MethodDesc *>, FALSE> EEThunkHashTable;

// ConstVirtualThunkSize declares the size of the code generated by
// CTPMethodTable::CreateThunkForVirtualMethod
#ifdef _X86_

static const DWORD ConstVirtualThunkSize    = sizeof(BYTE) + sizeof(DWORD) + sizeof(BYTE) + sizeof(LONG);

#elif defined(_PPC_)

static const DWORD ConstVirtualThunkSize    = 5 * sizeof(DWORD);

#else
PORTABILITY_WARNING("Remoting thunk size not defined for this platform.")
static const DWORD ConstVirtualThunkSize    = sizeof(LPVOID);
#endif

extern "C"
{
    UINT_PTR __stdcall CRemotingServices__CheckForContextMatch(Object* pStubData);
    void __stdcall CRemotingServices__DispatchInterfaceCall(MethodDesc* pMD);
    void __stdcall CRemotingServices__CallFieldGetter(MethodDesc *pMD, LPVOID pThis, LPVOID pFirst, LPVOID pSecond, LPVOID pThird);
    void __stdcall CRemotingServices__CallFieldSetter(MethodDesc *pMD, LPVOID pThis, LPVOID pFirst, LPVOID pSecond, LPVOID pThird);
}

extern "C" LPVOID __stdcall CTPMethodTable__CallTargetHelper2(const void *pTarget, LPVOID pvFirst, LPVOID pvSecond);
extern "C" LPVOID __stdcall CTPMethodTable__CallTargetHelper3(const void *pTarget, LPVOID pvFirst, LPVOID pvSecond, LPVOID pvThird);
extern "C" BOOL __stdcall CTPMethodTable__GenericCheckForContextMatch(Object* orTP);


// These are the values returned by RequiresManagedActivation
enum ManagedActivationType
{
    NoManagedActivation = 0,
    ManagedActivation   = 0x1,
};


struct timingData
{
    DWORD       threadId;
    BYTE        stage;
    __int64     cycleCount;
};


// This struct is also accessed from managed world
struct messageData
{
    PVOID       pFrame;
    MethodDesc  *pMethodDesc;
    MethodDesc  *pDelegateMD;
    MetaSig     *pSig;
    TypeHandle  thGoverningType;
    INT32       iFlags;
};


// The real proxy class is the class behind the 
// transparent proxy class
class CRealProxy
{
public:
    // Native helpers
    static FCDECL2(VOID, SetStubData, LPVOID pvRP, LPVOID pvStubData);
    static FCDECL1(LPVOID, GetStubData, LPVOID pvRP);        
    static FCDECL1(ULONG_PTR, GetStub, LPVOID pvRP);        
    static FCDECL0(LPVOID, GetDefaultStub);        
    static FCDECL1(Object*, GetProxiedType, Object* orRPUNSAFE);
    
    static VOID UpdateOptFlags(OBJECTREF refTP);
    static BOOL ProxyTypeIdentityCheck(MethodTable *pCliHierarchy, MethodTable *pSrvHierarchy);
};

// Forward declarations
class CVirtualThunkMgr;
class CNonVirtualThunkMgr;




// Class that provides various remoting services
// to the exposed world
class CRemotingServices
{
private:
    //+-------------------------------------------------------------------
    //
    //  Struct:     FieldArgs
    //
    //  Synopsis:   Structure to GC protect arguments for a field accessor call.
    //              DO NOT add non OBJECTREF data types in the structure
    //              see GCPROTECT_BEGIN() for a better explanation.
    //
    //+-------------------------------------------------------------------
    typedef struct _FieldArgs
    {
        OBJECTREF obj;
        OBJECTREF val;
        STRINGREF typeName;
        STRINGREF fieldName;        
    } FieldArgs;

public:
    // Native methods
    // See RemotingServices.cs for corresponding declarations.
    static FCDECL1(FC_BOOL_RET, FCIsTransparentProxy, Object* obj);
    static FCDECL1(Object*, FCGetRealProxy, Object* obj);
    static FCDECL1(Object*, FCUnwrap, Object* obj);
    static FCDECL1(Object*, FCAlwaysUnwrap, Object* obj);
    static FCDECL2(Object*, NativeCheckCast, Object* pObj, ReflectClassBaseObject* pType);
    static FCDECL0(VOID, SetRemotingConfiguredFlag);
    
    static FCDECL4(Object*, CreateTransparentProxy, Object* orRPUNSAFE, ReflectClassBaseObject* pClassToProxyUNSAFE, LPVOID pStub, Object* orStubDataUNSAFE);
    static FCDECL1(Object*, AllocateUninitializedObject, ReflectClassBaseObject* pClassOfObjectUNSAFE);
    static FCDECL1(VOID, CallDefaultCtor, Object* orefUNSAFE);

    static FCDECL1(Object*, AllocateInitializedObject, ReflectClassBaseObject* pClassOfObjectUNSAFE);
    static FCDECL1(VOID, ResetInterfaceCache, Object* orObjUNSAFE);
    
    // Methods related to interception of non virtual methods & virtual methods called
    // non virtually
    static TADDR    GetNonVirtualEntryPointForVirtualMethod(MethodDesc* pMD)
    {
        WRAPPER_CONTRACT;
        return *GetNonVirtualSlotForVirtualMethod(pMD);
    }
    static PTR_TADDR GetNonVirtualSlotForVirtualMethod(MethodDesc* pMD);
    
#ifndef HAS_REMOTING_PRECODE
    static Stub*    GetStubForNonVirtualMethod(MethodDesc* pMD, LPVOID pvAddrOfCode, Stub* pInnerStub);
#endif

    static void     DestroyThunk(MethodDesc* pMD);
    
    // Methods related to interception of interface calls
    static void GenerateCheckForProxy(CPUSTUBLINKER* psl);
    
    // Methods related to activation
    static BOOL         IsRemoteActivationRequired(TypeHandle ty);
    
    static OBJECTREF    CreateProxyOrObject(MethodTable *pMT, BOOL fIsCom = FALSE, BOOL fIsNewObj = FALSE);
    
    // Methods related to field accessors
    static void FieldAccessor(FieldDesc* pFD, OBJECTREF o, LPVOID pVal, BOOL fIsGetter);
    
    // Methods related to wrapping/unwrapping of objects
    static OBJECTREF WrapHelper(OBJECTREF obj);
    
    static OBJECTREF Wrap(OBJECTREF obj);
    static OBJECTREF GetProxyFromObject(OBJECTREF obj);
    static OBJECTREF GetObjectFromProxy(OBJECTREF obj, BOOL fMatchContexts);
    static BOOL IsProxyToRemoteObject(OBJECTREF obj);
    static OBJECTREF GetServerContext(OBJECTREF obj);
    
    // Methods related to creation and marshaling of appdomains
    static OBJECTREF CreateProxyForDomain(AppDomain *pDomain);

    // Extract the true class of a proxy
    static REFLECTCLASSBASEREF GetClass(OBJECTREF pThis);

    // Initialization function. This sets all the fields to NULL.
    static VOID Initialize();

    // Start up function. This actually starts up the remoting services.
    static void EnsureRemotingStarted();

    // Other helper functions.
    inline static MethodDesc *MDofPrivateInvoke()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pRPPrivateInvoke;
    }
    
    inline static MethodDesc *MDofInvokeStatic()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pRPInvokeStatic;
    }
    
    inline static MethodDesc *MDofIsCurrentContextOK()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pIsCurrentContextOK;
    }
    
    inline static MethodDesc *MDofCheckCast()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pCheckCast;
    }
    
    inline static MethodDesc *MDofWrap()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pWrapMethodDesc;
    }
    
    inline static MethodDesc *MDofFieldSetter()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pFieldSetterDesc;
    }
    
    inline static MethodDesc *MDofFieldGetter()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pFieldGetterDesc;
    }
    
    inline static MethodDesc *MDofGetType()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pGetTypeDesc;
    }

    inline static MethodDesc *MDofObjectGetType()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pObjectGetTypeDesc; 
    }


    inline static DWORD GetTPOffset()
    {
        LEAF_CONTRACT;
        return s_dwTPOffset;
    }

    inline static BOOL IsInstanceOfServerIdentity(MethodTable* pMT)
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(pMT));
        }
        CONTRACTL_END;
        
        return s_pServerIdentityClass == pMT;
    }
    
    inline static BOOL IsInstanceOfContext(MethodTable* pMT)
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(pMT));
        }
        CONTRACTL_END;
        
        return s_pContextClass == pMT;
    }
    
    inline static MethodTable *GetMarshalByRefClass()
    {
        CONTRACT (MethodTable*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pMarshalByRefObjectClass;
    }

    static INT32 IsTransparentProxy(Object* obj);
    static Object* GetRealProxy(Object* obj);
    static Object* AlwaysUnwrap(Object* obj);
    static MethodTable *GetProxyAttributeClass();

    static BOOL CheckCast(OBJECTREF orTP, TypeHandle ty);
    static BOOL CheckCast(OBJECTREF orTP, TypeHandle objTy, TypeHandle ty);
    static OBJECTREF GetExposedContext();
    static AppDomain *GetServerDomainForProxy(OBJECTREF orTP);
    static Context *GetServerContextForProxy(OBJECTREF orTP);
    static int GetServerDomainIdForProxy(OBJECTREF orTP);
    static UINT_PTR CheckForContextMatch(Object* pStubData);

    static ManagedActivationType __stdcall RequiresManagedActivation(TypeHandle ty);        
    static BOOL IsRemotingStarted()
    {
        LEAF_CONTRACT;
        return s_fRemotingStarted;
    };
    
    static DWORD GetOffsetOfSrvIdentityInRP() { return s_dwSrvIdentityOffsetInRealProxy; }
    static DWORD GetOffsetOfCliIdentityInRP() { return s_dwIdOffset; }
    static DWORD GetOffsetOfTPOrObjInIdentity() { return s_dwTPOrObjOffsetInIdentity; }
    static DWORD GetOffsetOfLeaseInIdentity() { return s_dwLeaseOffsetInIdentity; }
    static DWORD GetOffsetOfURIInIdentity() { return s_dwURIOffsetInIdentity; }
    inline static MethodDesc *MDofRenewLeaseOnCall() {  return s_pRenewLeaseOnCallDesc; }

    static const BYTE *GetStubForInterfaceMethod(MethodDesc *pItfMD);
private:
    static void StartRemoting();
    static void CopyDestToSrc(LPVOID pDest, LPVOID pSrc, UINT cbSize);
    static void CallFieldAccessor(FieldDesc* pFD, OBJECTREF o, VOID * pVal,
                                  BOOL fIsGetter, BOOL fIsByValue, BOOL fIsGCRef,
                                  TypeHandle ty, TypeHandle fldTy,
                                  CorElementType fieldType, UINT cbSize);

    static void GetTypeAndFieldName(FieldArgs *pArgs, FieldDesc *pFD, TypeHandle thEnclosingClass);
    static BOOL MatchField(FieldDesc* pCurField, LPCUTF8 szFieldName);
    static OBJECTREF SetExposedContext(OBJECTREF newContext);
    static OBJECTREF GetServerIdentityFromProxy(OBJECTREF obj);
    inline static MethodDesc *MDOfCreateProxyForDomain()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pProxyForDomainDesc;
    }
    
    inline static MethodDesc *MDofGetServerContextForProxy()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pServerContextForProxyDesc;
    }
    
    inline static MethodDesc *MDofGetServerDomainIdForProxy()
    {
        CONTRACT (MethodDesc*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pServerDomainIdForProxyDesc;
    }
    
    static VOID InitActivationServicesClass();
    static VOID InitRealProxyClass();
    static VOID InitRemotingProxyClass();
    static VOID InitServerIdentityClass();
    static VOID InitProxyAttributeClass();
    static VOID InitIdentityClass();
    static VOID InitContextClass();
    static VOID InitMarshalByRefObjectClass();
    static VOID InitRemotingServicesClass();
    static VOID InitObjectClass();
    static BOOL InitLeaseClass();
    static VOID InitRuntimeTypeClass();

    static MethodTable *s_pMarshalByRefObjectClass;    
    static MethodTable *CRemotingServices::s_pServerIdentityClass;
    static MethodTable *CRemotingServices::s_pContextClass;
    static MethodTable *CRemotingServices::s_pProxyAttributeClass;
    static MethodTable *CRemotingServices::s_pRuntimeTypeClass;

    static MethodDesc *s_pRPPrivateInvoke;
    static MethodDesc *s_pRPInvokeStatic;
    static MethodDesc *s_pIsCurrentContextOK;
    static MethodDesc *s_pCheckCast;
    static MethodDesc *s_pWrapMethodDesc;    
    static MethodDesc *s_pFieldSetterDesc;
    static MethodDesc *s_pFieldGetterDesc;
    static MethodDesc *s_pObjectGetTypeDesc;
    static MethodDesc *s_pGetTypeDesc;
    static MethodDesc *s_pProxyForDomainDesc;
    static MethodDesc *s_pServerContextForProxyDesc;
    static MethodDesc *s_pServerDomainIdForProxyDesc;
    static MethodDesc *s_pRenewLeaseOnCallDesc;
    static DWORD s_dwTPOffset;
    static DWORD s_dwIdOffset;    
    static DWORD s_dwServerOffsetInRealProxy;
    static DWORD s_dwSrvIdentityOffsetInRealProxy;
    static DWORD s_dwServerCtxOffset;
    static DWORD s_dwTPOrObjOffsetInIdentity;
    static DWORD s_dwLeaseOffsetInIdentity;
    static DWORD s_dwURIOffsetInIdentity;
    static DWORD s_dwMBRIDOffset;
    static CrstStatic s_RemotingCrst;
    static BOOL s_fRemotingStarted;
    
    
};

// Class that manages transparent proxy thunks
class CVirtualThunks
{
public:
    inline static void Initialize()
    {
        LEAF_CONTRACT;
        s_pVirtualThunks = NULL;
    }
    
    // Destructor
    static void DestroyVirtualThunk(CVirtualThunks *pThunk)
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(pThunk));
        }
        CONTRACTL_END;
        
        ::ClrVirtualFree(pThunk, 0, MEM_RELEASE);
    }
    
    inline static CVirtualThunks* GetVirtualThunks()
    {
        CONTRACT (CVirtualThunks*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN s_pVirtualThunks;
    }
    
    inline static CVirtualThunks* SetVirtualThunks(CVirtualThunks* pThunks) 
    {
        CONTRACT (CVirtualThunks*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(pThunks));
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN (s_pVirtualThunks = pThunks);
    }

    inline CVirtualThunks* GetNextThunk()
    {
        CONTRACT (CVirtualThunks*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN _pNext;
    }

    // Public member variables
    CVirtualThunks *_pNext;
    DWORD _dwReservedThunks;
    DWORD _dwStartThunk;
    DWORD _dwCurrentThunk;
    

    struct tagThunkCode
    {
        BYTE pCode[ConstVirtualThunkSize];
    } ThunkCode[1];

private:
    // Cannot be created
    CVirtualThunks(CVirtualThunks *pNext, DWORD dwCommitedSlots, DWORD dwReservedSlots, DWORD dwStartSlot, DWORD dwCurrentSlot)
    {
        LEAF_CONTRACT;
    }

    // Private statics
    static CVirtualThunks *s_pVirtualThunks;
};


#ifndef HAS_REMOTING_PRECODE

class CNonVirtualThunk
{
public: 
    // Constructor
    CNonVirtualThunk(const BYTE* pbCode)
    : _addrOfCode(pbCode), _pNext(NULL)
    {
        WRAPPER_CONTRACT;
    }
    
    ~CNonVirtualThunk();
    
    inline LPVOID*  GetAddrOfCode()
    {
        CONTRACT (LPVOID*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN (LPVOID*)&_addrOfCode;
    }

    inline const BYTE* GetThunkCode()
    {
        CONTRACT (const BYTE*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN _addrOfCode;
    }
    
    inline CNonVirtualThunk* GetNextThunk()
    {
        CONTRACT (CNonVirtualThunk*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN _pNext;
    }
    
    static void Initialize();
    static CNonVirtualThunk* AddrToThunk(LPVOID pAddr);
    inline static CNonVirtualThunk* GetNonVirtualThunks()
    {
        CONTRACT (CNonVirtualThunk*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL, NULL_OK));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN s_pNonVirtualThunks;
    }
    
    static CNonVirtualThunk* SetNonVirtualThunks(const BYTE* pbCode); 

    const BYTE* _addrOfCode;
    
private:    

    void SetNextThunk();

    // Private statics
    static CNonVirtualThunk *s_pNonVirtualThunks;

    // Private members
    CNonVirtualThunk* _pNext;
};

inline void CNonVirtualThunk::Initialize() 
{ 
    LEAF_CONTRACT;
    s_pNonVirtualThunks = NULL; 
}

inline void CNonVirtualThunk::SetNextThunk()  
{
    LEAF_CONTRACT;

    _pNext = s_pNonVirtualThunks; 
    s_pNonVirtualThunks = this;
}

inline CNonVirtualThunk* CNonVirtualThunk::AddrToThunk(LPVOID pAddr)
{
    CONTRACT (CNonVirtualThunk*)
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(CheckPointer(pAddr));
        POSTCONDITION(CheckPointer(RETVAL));
    }
    CONTRACT_END;
    
    RETURN (CNonVirtualThunk *)((size_t)pAddr - (size_t)offsetof(CNonVirtualThunk, _addrOfCode));
}

#endif // HAS_REMOTING_PRECODE


class CTPMethodTable
{
    friend class CRemotingServices;
    
public:
    // Public statics    
    static DWORD GetCommitedTPSlots()
    {
        LEAF_CONTRACT;
        return s_dwCommitedTPSlots;
    }
        
    static MethodTable *GetMethodTable()
    {
        CONTRACT (MethodTable*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN s_pThunkTable;
    }
    
    static MethodTable **GetMethodTableAddr()
    {
        CONTRACT (MethodTable**)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN &s_pThunkTable;
    }
    
    static VOID Initialize();
    static INT32 IsTPMethodTable(MethodTable *pMT);
    static TypeHandle GetClassBeingProxied(OBJECTREF pTP);    
    
#ifndef HAS_REMOTING_PRECODE
    static PTR_TADDR GetOrCreateNonVirtualSlotForVirtualMethod(MethodDesc* pMD);
    static const BYTE* CreateNonVirtualThunkForVirtualMethod(MethodDesc* pMD);                 
    static Stub* CreateStubForNonVirtualMethod(MethodDesc* pMD, CPUSTUBLINKER *psl, LPVOID pvAddrOfCode, Stub* pInnerStub);
#endif // HAS_REMOTING_PRECODE


    static OBJECTREF GetRP(OBJECTREF orTP);
    static LPVOID __stdcall CallTarget(MethodDesc* pTargetMD, LPVOID pvFirst, LPVOID pvSecond);
    static LPVOID __stdcall CallTarget(MethodDesc* pTargetMD, LPVOID pvFirst, LPVOID pvSecond, LPVOID pvThird);
    static BOOL CheckCastHelper(MethodDesc* pTargetMD, LPVOID pvFirst, LPVOID pvSecond);
    static BOOL CheckCast(MethodDesc* pTargetMD, OBJECTREF orTP, TypeHandle ty);
    static void RefineProxy(OBJECTREF orTP, TypeHandle ty);
    
    inline static Stub* GetTPStub()
    {
        CONTRACT (Stub*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
            SO_TOLERANT;
        }
        CONTRACT_END;
        
        RETURN s_pTPStub;
    }
    
    inline static Stub* GetDelegateStub()
    {
        CONTRACT (Stub*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN s_pDelegateStub;
    }
    
    inline static DWORD GetOffsetOfMT()
    {
        LEAF_CONTRACT;
        return s_dwMTOffset;
    }
    
    inline static DWORD GetOffsetOfInterfaceMT()
    {
        LEAF_CONTRACT;
        return s_dwItfMTOffset;
    }
    
    inline static DWORD GetOffsetOfStub()
    {
        LEAF_CONTRACT;
        return s_dwStubOffset;
    }
    
    inline static DWORD GetOffsetOfStubData()
    {
        LEAF_CONTRACT;
        return s_dwStubDataOffset;
    }

    inline static DWORD GetOffsetOfRP()
    {
        LEAF_CONTRACT;
        return s_dwRPOffset;
    }
    
    static void DestroyThunk(MethodDesc* pMD);

    // Interpretation of __TransparentProxy._stub
    typedef UINT_PTR CheckContextCrossingProc (Object*);

    inline static BOOL IsInstanceOfRemotingProxy(MethodTable *pMT) 
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(pMT));
        }
        CONTRACTL_END;
        
        return s_pRemotingProxyClass == pMT;
    }
    
    // This has to be public to access it from inline asm
    SPTR_DECL(Stub, s_pTPStub);

    static Stub *s_pDelegateStub;

private:

#ifndef DACCESS_COMPILE

    // Private statics    
    static void InitThunkTable(DWORD dwCommitedTPSlots, DWORD dwReservedTPSlots, MethodTable* pTPMethodTable)
    {
        CONTRACTL
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(pTPMethodTable));
        }
        CONTRACTL_END;
        
        s_dwCommitedTPSlots = dwCommitedTPSlots;
        s_dwReservedTPSlots = dwReservedTPSlots;
        s_pThunkTable = pTPMethodTable;    
    }

    
    static void DestroyThunkTable()
    {
        WRAPPER_CONTRACT;
        ::ClrVirtualFree(MTToAlloc(s_pThunkTable, s_dwGCInfoBytes), 0, MEM_RELEASE);
        s_pThunkTable = NULL;
        s_dwCommitedTPSlots = 0;
        s_dwReservedTPSlots = 0;
    }

#endif // #ifndef DACCESS_COMPILE
    
    static void EnsureFieldsInitialized();

    static void CreateTPOfClassForRP(TypeHandle ty, OBJECTREF *pRP, OBJECTREF *pTP);
    static void CreateTPMethodTable();    
    static BOOL ExtendCommitedSlots(DWORD dwSlots);
    static BOOL AllocateThunks(DWORD dwSlots, DWORD dwCommitSize);
#ifdef HAS_REMOTING_PRECODE
    static void ActivatePrecodeRemotingThunk();
#endif // HAS_REMOTING_PRECODE
    static void __stdcall PreCall(TPMethodFrame *pFrame AMD64_ARG(Object *_pTP));
    static PlatformDefaultReturnType __stdcall OnCall(TPMethodFrame *pFrame, Thread *pThrd, ARG_SLOT *pReturn);    
    static MethodTable *AllocToMT(BYTE *Alloc, LONG off)
    {
        CONTRACT (MethodTable*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(Alloc));
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN (MethodTable *) (Alloc + off);
    }
    
    static BYTE *MTToAlloc(MethodTable *MT, LONG off)
    {
        CONTRACT (BYTE*)
        {
            NOTHROW;
            GC_NOTRIGGER;
            MODE_ANY;
            PRECONDITION(CheckPointer(MT));
            POSTCONDITION(CheckPointer(RETVAL));
        }
        CONTRACT_END;
        
        RETURN (((BYTE *) MT) - off);
    }
    
    static void CreateThunkForVirtualMethod(DWORD dwSlot, BYTE *bCode);    
    static Stub *CreateTPStub();
    static Stub *CreateDelegateStub();
    static void EmitCallToStub(CPUSTUBLINKER* pStubLinker, CodeLabel* pCtxMismatch);
    static void EmitJumpToAddressCode(CPUSTUBLINKER* pStubLinker, CodeLabel* ConvMD, CodeLabel* UseCode);
    static void EmitSetupFrameCode(CPUSTUBLINKER *pStubLinker);
    
    // Static members    
    static DWORD s_dwCommitedTPSlots;
    static DWORD s_dwReservedTPSlots;
    static MethodTable* s_pThunkTable;
    static MethodTable* s_pRemotingProxyClass;
    static EEClass *s_pTransparentProxyClass;
    static DWORD s_dwGCInfoBytes;
    static DWORD s_dwMTDataSlots;
    static DWORD s_dwRPOffset;    
    static DWORD s_dwMTOffset;
    static DWORD s_dwItfMTOffset;
    static DWORD s_dwStubOffset;
    static DWORD s_dwStubDataOffset;
    static DWORD s_dwMaxSlots;
    static MethodTable *s_pTPMT;    
    static CrstStatic s_TPMethodTableCrst;
    static EEThunkHashTable *s_pThunkHashTable;
    static BOOL s_fTPTableFieldsInitialized;

    enum
    {
        CALLTYPE_INVALIDCALL        = 0x0,          // Important:: sync this with RealProxy.cs
        CALLTYPE_METHODCALL         = 0x1,          // Important:: sync this with RealProxy.cs
        CALLTYPE_CONSTRUCTORCALL    = 0x2           // Important:: sync this with RealProxy.cs
    };
};

inline TypeHandle CTPMethodTable::GetClassBeingProxied(OBJECTREF pTP)
{
    CONTRACTL
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        PRECONDITION(pTP != NULL);
    }
    CONTRACTL_END;
    
    _ASSERTE(pTP->GetMethodTable()->IsTransparentProxyType());
    return TypeHandle((MethodTable *) pTP->GetPtrOffset(s_dwMTOffset));
}

// Returns the one and only transparent proxy stub
inline Stub* TheTPStub()
{
    CONTRACT (Stub*)
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        POSTCONDITION(CheckPointer(RETVAL));
    }
    CONTRACT_END;
    
    RETURN CTPMethodTable::GetTPStub();
}

// Returns the one and only delegate stub
inline Stub* TheAsyncDelegateStub()
{
    CONTRACT (Stub*)
    {
        NOTHROW;
        GC_NOTRIGGER;
        MODE_ANY;
        POSTCONDITION(CheckPointer(RETVAL));
    }
    CONTRACT_END;
    
    RETURN CTPMethodTable::GetDelegateStub();
}

// These stub manager classes help the debugger to step
// through the various stubs and thunks generated by the
// remoting infrastructure
class CVirtualThunkMgr :public StubManager
{
    friend class CTPMethodTable;

    VPTR_VTABLE_CLASS(CVirtualThunkMgr, StubManager)

public:
    static void InitVirtualThunkManager(const BYTE* stubAddress);
#ifndef DACCESS_COMPILE
    CVirtualThunkMgr(const BYTE *address) : _stubAddress(address)
    {
        WRAPPER_CONTRACT;
    }
#endif

protected:
#ifdef _DEBUG
    virtual const char * DbgGetName() { return "CVirtualThunkMgr"; }
#endif


    virtual BOOL CheckIsStub_Internal(TADDR stubStartAddress);

    virtual BOOL DoTraceStub(const BYTE *stubStartAddress, TraceDestination *trace) DAC_EMPTY_RET(FALSE);
    virtual MethodDesc *Entry2MethodDesc(const BYTE *StubStartAddress, MethodTable *pMT) DAC_EMPTY_RET(NULL);

private:
    // Private methods
    LPBYTE FindThunk(const BYTE *stubStartAddress);
    static MethodDesc *GetMethodDescByASM(const BYTE *startaddr, MethodTable *pMT);
    static BOOL IsThunkByASM(const BYTE *startaddr);

    // Private statics
    static CVirtualThunkMgr *s_pVirtualThunkMgr;

    // Private member variables
    const BYTE *_stubAddress;
#ifdef DACCESS_COMPILE
protected:
    virtual LPCWSTR GetStubManagerName(TADDR addr)
        { LEAF_CONTRACT; return L"CVirtualThunk"; }
#endif
};


#ifdef HAS_REMOTING_PRECODE

// Dummy CNonVirtualThunkMgr to make DAC compile
class CNonVirtualThunkMgr :public CVirtualThunkMgr
{
    VPTR_VTABLE_CLASS(CNonVirtualThunkMgr, CVirtualThunkMgr)

protected:
    virtual BOOL CheckIsStub_Internal(TADDR stubStartAddress) { return FALSE; }
};

#else // HAS_REMOTING_PRECODE

class CNonVirtualThunkMgr :public StubManager
{
    friend class CTPMethodTable;

    VPTR_VTABLE_CLASS(CNonVirtualThunkMgr, StubManager)
    
public:
    static void InitNonVirtualThunkManager();

protected:
#ifdef _DEBUG
    virtual const char * DbgGetName() { return "CNonVirtualThunkMgr"; }
#endif

    virtual BOOL CheckIsStub_Internal(TADDR stubStartAddress);

    virtual BOOL DoTraceStub(const BYTE *stubStartAddress, TraceDestination *trace) DAC_EMPTY_RET(FALSE);

    virtual BOOL TraceManager(Thread *thread,
                              TraceDestination *trace,
                              CONTEXT *pContext,
                              BYTE **pRetAddr) DAC_EMPTY_RET(FALSE);
    
    virtual MethodDesc *Entry2MethodDesc(const BYTE *StubStartAddress, MethodTable *pMT) DAC_EMPTY_RET(NULL);
    
private:
    // Private methods
    CNonVirtualThunk* FindThunk(const BYTE *stubStartAddress);
    static MethodDesc *GetMethodDescByASM(const BYTE *startaddr);
    static BOOL IsThunkByASM(const BYTE *startaddr);

    // Private statics
    static CNonVirtualThunkMgr *s_pNonVirtualThunkMgr;
#ifdef DACCESS_COMPILE
protected:
    virtual LPCWSTR GetStubManagerName(TADDR addr)
        { LEAF_CONTRACT; return L"CNonVirtualThunk"; }
#endif
};

#endif // HAS_REMOTING_PRECODE


#endif // __REMOTING_H__
